package libtool65.o65;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

public class Relocation {
//	{ [255,...,255,] offset of next relocation (b), typebyte|segmentID [, low_byte] }+
	private int offset = 0;
//	where typebyte has the bits 5, 6 and 7 and is one of
	final static int TYPE_MASK = 0xE0;
//	WORD	$80	2 byte address
	public final static int TYPE_WORD = 0x80;
//	HIGH	$40	high byte of an address
	public final static int TYPE_HIGH = 0x40;
//	LOW	$20	low byte of an address
	public final static int TYPE_LOW = 0x20;
//	SEGADR	$c0	3 byte address (65816)
	final static int TYPE_SEGADR = 0xC0;
//	SEG	$a0	segment byte of 3 byte address
	final static int TYPE_SEG = 0xA0;
//	The segmentID stands for the segment the reference points to: 
//	0		undefined
	public final static int SEGMENT_UNDEFINED = 0;
//	1		absolute value
	public final static int SEGMENT_ABSOLUTE = 1;
//	2		text segment
	public final static int SEGMENT_TEXT = 2;
//	3		data segment
	public final static int SEGMENT_DATA = 3;
//	4		bss segment
	public final static int SEGMENT_BSS = 4;
//	5		zero segment
	public final static int SEGMENT_ZERO = 5;
	private int type = 0;
	private int low_byte = 0;
	private int seg_index = 0;
	private int abs_offset = 0;
	private int current_low = 0;

	public void read(InputStream file, Mode mode) throws IOException, UnknownRelocationTypeException {
		offset = 0;
		low_byte = 0;
		int offset_part = file.read();
		if (offset_part != 0)
		{
			while (offset_part > 254)
			{
				offset += 254;
				offset_part = file.read();
			}
			offset += offset_part;
			type = file.read();
			if ((getSegment()) == SEGMENT_UNDEFINED)
				seg_index = mode.readSize(file);
			switch (getType())
			{
			case TYPE_WORD: break;
			case TYPE_HIGH:
				if (!mode.isPageRealloc())
				{
					low_byte = file.read();
				}
				break;
			case TYPE_LOW: break;
			case TYPE_SEGADR: break;
			case TYPE_SEG:
				low_byte = file.read();
				low_byte |= file.read() << 8;
				break;
			default:
				throw new UnknownRelocationTypeException(type);
			}
		}
	}

	public boolean isEmpty() {
		return offset == 0;
	}

	public void dumpInfo(PrintStream out) {
		out.println("offset="+offset+"(rel)"+abs_offset+"(abs) type="+getTypeString()+" seg="+getSegmentString()+" low byte="+low_byte);
	}

	private String getTypeString() {
		switch (getType())
		{
		case TYPE_WORD: return "WORD";
		case TYPE_HIGH: return "HIGH";
		case TYPE_LOW: return "LOW";
		case TYPE_SEGADR: return "SEGADR";
		case TYPE_SEG: return "SEG";
		default: return "unknown("+(getType())+")";
		}
	}

	private String getSegmentString() {
		return getSegmentString(getSegment(), seg_index);
	}

	public static String getSegmentString(int index, int seg_index) {
		switch (index)
		{
		case SEGMENT_UNDEFINED: return "UNDEFINED("+seg_index+")";
		case SEGMENT_ABSOLUTE: return "ABSOLUTE";
		case SEGMENT_TEXT: return "TEXT";
		case SEGMENT_DATA: return "DATA";
		case SEGMENT_BSS: return "BSS";
		case SEGMENT_ZERO: return "ZERO";
		default: return "unknown("+index+")";
		}
	}

	public int addOffset(int current_offset) {
		abs_offset = current_offset + offset;
		return abs_offset;
	}

	public int getLowByte() {
		return low_byte;
	}

	public int getAbsOffset() {
		return abs_offset;
	}

	public int getSegment() {
		return type & ~TYPE_MASK;
	}

	public int getType() {
		return type & TYPE_MASK;
	}

	public int getSegmentIndex() {
		return seg_index;
	}

	public void setCurrentLow(int low) {
		current_low  = low;
	}
	
	public int getCurrentLow() {
		return current_low;
	}

}
